package com.sweet.logging.appender;

import com.sweet.logging.logger.LoggingEvent;
import com.sweet.logging.nio.SweetRemoteClient;

import javax.naming.ConfigurationException;
import java.io.IOException;
import java.util.Properties;
import java.util.concurrent.ArrayBlockingQueue;

/**
 *
 * 向远程的日志收集器推送日志
 *
 * @author xiexiaofeng
 * @version 1.0     2017年09月08日  11点53分
 */
public class RemoteAppender implements Appender
{
    public static final String CONFIG_NAME = "remote";

    private static final String ADDRESS      = "sweet.appender.remote.address";
    private static final String QUEUE_LENGTH =  "sweet.appender.remote.queue.length";
    /**
     * 这里用固定长度的阻塞队列，如果客户端输出日志过多处理不过来，考虑写入本地文件，不能阻塞
     * 客户端线程。数组型的队列，在频繁的存取元素中, 不会给 yong GC 带来很大压力。缺点是大小固定。
     */
    private volatile ArrayBlockingQueue<LoggingEvent> queue;

    private SweetRemoteClient client;

    /**
     * 读取配置参数进行初始化
     * @param properties 配置文件，按照约定放在类路径下
     * @throws ConfigurationException 配置文件有错误
     * @throws IOException 暂未可知
     */
    public RemoteAppender(Properties properties) throws ConfigurationException, IOException
    {
        Object address = properties.get(ADDRESS);
        if (address == null)
            throw new ConfigurationException("the config param `"+ ADDRESS +"` not set");
        String[] ipPort = address.toString().split(":");
        if (ipPort.length == 0)
            throw new ConfigurationException("the config param `"+ ADDRESS +"="+ address +"` not correct");
        Object lengthStr = properties.get(QUEUE_LENGTH);
        if (lengthStr == null)
            throw new ConfigurationException("the config param `"+QUEUE_LENGTH+"` not set");
        int length = Integer.parseInt(lengthStr.toString());
        queue = new ArrayBlockingQueue<>(length);
        client = new SweetRemoteClient(ipPort[0], ipPort.length == 1 ? 80 : Integer.parseInt(ipPort[1]), this);
        client.start(); // 开启连接
    }

    /**
     * 输出到远程，这里先放进队列 会由 {@link com.sweet.logging.nio.SweetRemoteClient}
     * 进行异步发送
     * @param event 写日志事件
     */
    @Override
    public void write(LoggingEvent event)
    {
        try
        {
            queue.add(event);
        } catch (IllegalStateException e) {
            // 队列满了，考虑写入本地磁盘
        }
    }

    /**
     * 这个方法会被异步调用
     */
    public LoggingEvent take()
    {
        try
        {
            return queue.take(); // 如果队列空了，则线程会进入等待，避免空转，导致CPU开销太大
        } catch (InterruptedException e)
        {
            return null; // 忽略这个异常
        }
    }


    public void close()
    {
        client.close();
    }

}
